package br.com.iswe.core.jasperreports;

import java.util.List;
import java.util.Map;

import net.sf.jasperreports.engine.JRDataset;
import net.sf.jasperreports.engine.JRRuntimeException;
import net.sf.jasperreports.engine.query.JRClauseFunction;
import net.sf.jasperreports.engine.query.JRClauseTokens;
import net.sf.jasperreports.engine.query.JRQueryClauseContext;
import net.sf.jasperreports.engine.query.JRSqlAbstractInClause;

public class JRHibernateQueryExecuter extends
		net.sf.jasperreports.engine.query.JRHibernateQueryExecuter {

	@SuppressWarnings("unchecked")
	public JRHibernateQueryExecuter(JRDataset dataset, Map parameters) {
		super(dataset, parameters);
	}

	@SuppressWarnings("unchecked")
	private ISWEJasperReportScriptlet extractScriptletClass(JRDataset dataset) {
		ISWEJasperReportScriptlet retorno = null;
		try {
			ClassLoader classLoader = Thread.currentThread()
					.getContextClassLoader();
			Class clazz = classLoader.loadClass(dataset.getScriptletClass());
			Object instance = clazz.newInstance();
			if (instance instanceof ISWEJasperReportScriptlet) {
				retorno = (ISWEJasperReportScriptlet) instance;
			}
		} catch (Exception e) {
			retorno = null;
		}
		return retorno;
	}

	@SuppressWarnings("unchecked")
	@Override
	protected JRClauseFunction resolveFunction(String id) {
		// carrega as funcoes Basicas
		clauseFunctions.put("IN", new HibernateInFunction("in"));
		clauseFunctions.put("NOTIN", new HibernateInFunction("not in"));
		
		JRClauseFunction function = (JRClauseFunction) clauseFunctions.get(id);
		if (function == null) {
			// tenta obter do scriptlet
			ISWEJasperReportScriptlet scriptlet = extractScriptletClass(super.dataset);
			if (scriptlet != null) {
				function = scriptlet.resolveFunction(id);
			}
		}
		if (function == null) {
			return super.resolveFunction(id);
		}
		clauseFunctions.put(id, function);// mantem no cache
		return function;
	}

	class HibernateInFunction extends JRSqlAbstractInClause {
		private String operator;

		public HibernateInFunction(String operator) {
			this.operator = operator;
		}

		@Override
		protected void appendInOperator(StringBuffer sBuffer) {
			sBuffer.append(" ");
			sBuffer.append(operator);
			sBuffer.append(" ");
		}

		public void apply(JRClauseTokens clauseTokens,
				JRQueryClauseContext queryContext) {
			String col = clauseTokens.getToken(POSITION_DB_COLUMN);
			String param = clauseTokens.getToken(POSITION_PARAMETER);

			if (col == null) {
				throw new JRRuntimeException(
						"SQL IN clause missing DB column token");
			}

			if (param == null) {
				throw new JRRuntimeException(
						"SQL IN clause missing parameter token");
			}

			StringBuffer sbuffer = queryContext.queryBuffer();

			Object paramValue = queryContext.getValueParameter(param).getValue();
			if (paramValue == null) {
				handleNoValues(queryContext);
			} else {
				int count = valuesCount(param, paramValue);
				if (count == 0) {
					handleNoValues(queryContext);
				} else {
					sbuffer.append(col);
					sbuffer.append(' ');
					appendInOperator(sbuffer);
					sbuffer.append(' ');
					sbuffer.append('(');
					for (int idx = 0; idx < count; ++idx) {
						if (idx > 0) {
							sbuffer.append(", ");
						}
						sbuffer.append(getParameterValueAt(idx, paramValue));
					}
					sbuffer.append(')');

					//queryContext.addQueryMultiParameters(param, count);
				}
			}
		}

		@SuppressWarnings("unchecked")
		private String getParameterValueAt(int idx, Object paramValue) {
			if (paramValue.getClass().isArray())
			{
				Object[] objectArray = (Object[])paramValue;
				return objectArray[idx].toString();
			}
			else if (paramValue instanceof List)
			{
				List list = (List) paramValue;
				return list.get(idx).toString();
			}
			return null;
		}
	}

}
